<?php
/**
 * @author Jhonatan S. Serafim
 * @since 04/07/2011
 * @since 03/09/2011
 */
class SGObjetoManagerVO extends SGObjetoManager {
	
	protected $generate;
	
	public function __construct(SGObjetoConexao $conn){
		parent::__construct($conn);
		$this->generate =& new SGObjetoGenerate();
	}
	
	private function handleInvalidObject($msg=false){
		throw new SGObjetoInvalidObjectException($msg ? $msg : "conexao.erro.objeto.invalido");
	}
	
	private function handleInvalidSQL($msg=false){
		throw new SGObjetoInvalidSQLException($msg ? $msg : "conexao.erro.geracao.sql");
	}
	
	private function verifyVO(SGObjeto $vo){
		if(!($vo instanceof SGObjetoVO))
			$this->handleInvalidObject();
	}
	
	private function verifyAndApplyNextIdVO(SGObjetoVO $vo){
		if($vo->getNextId()){
			$vars =& $vo->getVars();
			foreach ($vars as $key => $var)
				if($var->getPrimaryKey() && !$var->getForeignKey())
					$var->setValor($this->conn->nextid($vo->getNomeTabela()));
		}
	}
	
	private function decodeValue(&$value, $type){
		return $this->formatValue($value, $type);
	}
	
	private function formatValue(&$value, $type){
		if($value || $value === '0'){
			switch ($type) {
				case SGObjetoVariavel::DOUBLE:
					$value = str_replace(".","",$value);
					$value = str_replace(",",".",$value);
				break;
				case SGObjetoVariavel::DATE:
					list($ano, $mes, $dia) = explode("-",$value);
					if(!$ano || !$mes || !dia) 
						$value = '';
					else
						$value = ($dia.'/'.$mes.'/'.$ano);
				break;
				case SGObjetoVariavel::DATETIME:
					list($data, $hora) = explode(" ",$value);
					if(!$data || !$hora)
						$value = '';
					else{
						$data = $this->decodeValue($data, SGObjetoVariavel::DATE);
						$value = $data.' '.$hora;
					}
				break;
			}
		}
		
		return $value;
	}
	
	public function queryOne($sql){
		$resultado = $this->conn->queryOne($sql);
		return $resultado;
	}
	
	public function execute($sql){
		$res = $this->conn->execute($sql);
		return $res;
	}
	
	public function inserir(SGObjeto $vo){
		$this->verifyAndApplyNextIdVO($vo);
		return $this->persistir($vo, SGObjetoManagerVO::INSERIR, $whereLivre);
	}
	
	public function atualizar(SGObjeto $vo){
		return $this->persistir($vo, SGObjetoManagerVO::ATUALIZAR);
	}
	
	public function deletar(SGObjeto $vo, $whereLivre=false){
		$this->persistir($vo, SGObjetoManagerVO::DELETAR, $whereLivre);
	}
	
	private function persistir(SGObjetoVO $vo, $tipo, $whereLivre=false, $comBusca=fase){
		$this->verifyVO($vo);
		
		$sql = '';
		
		switch ($tipo) {
			case SGObjetoManagerVO::INSERIR: $sql = $this->generate->inserir($vo); break;
			case SGObjetoManagerVO::ATUALIZAR: $sql = $this->generate->atualizar($vo); break;
			case SGObjetoManagerVO::DELETAR: $sql = $this->generate->deletar($vo, $whereLivre, $comBusca); break;
		}
		
		$res = $this->execute($sql);
		
		$this->conn->free($res);
		
		return $vo;
	}
	
	public function selecionar(SGObjeto $vo){
		$this->verifyVO($vo);
		
		$sql = $this->generate->selecionar($vo);
		
		$invalidSQL = false;
		
		$res = $this->execute($sql);
		
		if($this->conn->numRows($res) <= 1) $are = $this->conn->fetchRow($res);
		else $invalidSQL = true;
		
		$this->conn->free($res);
		
		if($invalidSQL) $this->handleInvalidSQL('conexao.erro.geracao.sql.muitos.valores.retornados');
		
		return $this->popularVO($are, $vo::newInstance());
	}
	
	private function popularVO($are, SGObjetoVO $vo){
		$vars =& $vo->getVars();
		
		foreach ($vars as $key => $var){
			$var->setValor($this->decodeValue($are->{strtoupper($var->getNome())}, $var->getTipo()));
			
			if($var instanceof SGObjetoVariavelObject){
				$var->getInstanciaObjeto()->getVariavelByNome($var->getNome())->setValor($var->getValor());

				$var->setInstanciaObjeto($this->selecionar($var->getInstanciaObjeto()));
			}
		}
			
		return $vo;
	}
	
	public function listar(SGObjeto $vo, $pagina=0, $maxLinhas=0){
		$this->verifyVO($vo);
		
		$sql = $this->generate->selecionar($vo, true, false);
		
		if($pagina > 0 && $maxLinhas > 0)
			$sql = $this->generate->paginar($sql, $pagina, $maxLinhas);
		
		$res = $this->execute($sql);
		
		$vos =& new ArrayObject();
		if(((int) $this->conn->numRows($res)) == 0) return $vos;
		
		while($res && $are = $this->conn->fetchRow($res))
			$vos->append($this->popularVO($are, $vo::newInstance()));
		
		$this->conn->free($res);
		
		return $vos;
	}
	
	public function contar(SGObjeto $vo){
		$this->verifyVO($vo);
		
		$sql = $this->generate->selecionar($vo, true, true);
		
		return $this->queryOne($sql);
	}
	
}

class SGObjetoGenerate extends SGObjeto {
	
	public function inserir(SGObjetoVO $vo){
		$sqlNomes = '';
		$sqlValores = '';
		
		$vars =& $vo->getVars();
		foreach ($vars as $key => $var) {
			if($var->getValor() || $var->getValor() === '0'){
				if(!$sqlNomes) {
					$sqlNomes .= '(';
					$sqlValores .= '(';
				}
			
				$sqlNomes .= $var->getNome() . ', ';
				$sqlValores .= $this->encodeValue($var->getValor(), $var->getTipo(), false, false) . ', ';
			}
		}
	
		if(!$sqlNomes) $this->handleInvalidSQL();
		else{
			$sqlNomes = substr($sqlNomes, 0, -2) . ')';
			$sqlValores = substr($sqlValores, 0, -2) . ')';
		}
	
		return 'INSERT INTO ' . $vo->getNomeTabela() . ' ' . $sqlNomes . ' VALUES ' . $sqlValores;
	}
	
	public function atualizar(SGObjetoVO $vo){
		$sql = '';
	
		$vars =& $vo->getVars();
		foreach ($vars as $key => $var)
			if($var->getValor() || $var->getValor() === '0')
			$sql .= $var->getNome() . $this->encodeValue($var->getValor(), $var->getTipo(), true, false) . ', ';
	
		if(!$sql) $this->handleInvalidSQL();
		else $sql = substr($sql, 0, -2);
	
		return 'UPDATE ' . $vo->getNomeTabela() . ' SET ' . $sql  . $this->where($vo, false, true, false);
	}
		
	public function deletar(SGObjetoVO $vo, $whereLivre=false, $comBusca=false){
		return 'DELETE FROM ' . $vo->getNomeTabela() . $this->where($vo, $whereLivre, true, $comBusca);
	}
	
	public function selecionar(SGObjetoVO $vo, $listar=false, $contar=false, $comBusca=false){
		$where = $this->where($vo, true, true, $comBusca);
// 		if(!$where && !$listar) $this->handleInvalidSQL();
		return 'SELECT ' . $this->campos($vo, $contar) . ' FROM ' . $vo->getNomeTabela()  . $where . (!$listar? ' LIMIT 1' : '');
	}

	public function paginar($sql, $pagina, $maxLinhas){
		$inicio = (((int) $pagina) * ((int) $maxLinhas)) - ((int) $maxLinhas);
		if($inicio < 0) $inicio = 0;
		$fim = (int) $maxLinhas;
		return 'SELECT * FROM ('.$sql.') paginacao LIMIT '.$inicio.', '.$fim;
	}
	
	private function where(SGObjetoVO $vo, $whereLivre=false, $comOperador=true, $comBusca=false){
		$sql = '';
	
		$vars =& $vo->getVars();
		foreach ($vars as $key => $var)
			if(($var->getValor() || $var->getValor() === '0')  && ($whereLivre || $var->getPrimaryKey()))
				$sql .= $var->getNome() . $this->encodeValue($var->getValor(), $var->getTipo(), $comOperador, $comBusca) . ' AND ';
	
		if($sql) $sql = substr($sql, 0, -5);
		else 
			return $sql;
	
		return ' WHERE ' .  $sql;
	}
	
	private function campos(SGObjetoVO $vo, $contar=false){
		if($contar)
			return ' COUNT(*) AS qtd ';
		
		$vars =& $vo->getVars();
		foreach ($vars as $key => $var)
			$sql .= $var->getNome() . ', ';
		
		$sql = substr($sql, 0, -2);
		return $sql;
	}
	
	private function encodeValue(&$value, $type, $comOperador, $comBusca){
		if($value || $value === '0'){
			if($type == SGObjetoVariavel::STRING)
				return ($comOperador && $comBusca)
						? ' LIKE \'%'.$value.'%\''
						: (($comOperador)
							? ' = \''.$value.'\''
							: '\''.$value.'\'');
			
			$value = '\''.$this->formatValue($value, $type).'\'';
			
			if($comOperador) $value = ' = ' . $value;
		}else
			$value = 'NULL';
		
		return $value;
	}
	
	private function formatValue(&$value, $type){
		if($value || $value === '0'){
			switch ($type) {
				case SGObjetoVariavel::DOUBLE:
					$value = str_replace(".","",$value);
					$value = str_replace(",",".",$value);
				break;
				case SGObjetoVariavel::DATE:
					$value = explode("/",$value);
					$value = ($value[2].'-'.$value[1].'-'.$value[0]);
				break;
				case SGObjetoVariavel::DATETIME:
					list($data, $hora) = explode(" ",$value);
					$data = $this->formatValue($data, SGObjetoVariavel::DATE);
					$value = $data.' '.$hora;
				break;
			}
		}
				
		return $value;
	}
	
	private function handleInvalidSQL($msg=false){
		throw new SGObjetoInvalidSQLException($msg ? $msg : "conexao.erro.geracao.sql");
	}
	
}
?>